/* OTA example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "stc_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
#include "stc_https_ota.h"

#include "nvs.h"
#include "nvs_flash.h"

#include "esp_log.h"
#include "driver/uart.h"

 #include "mqtt_client.h"
 #include "cJSON.h"



// #include "esp_crc.h"

// #include "lwip/netif.h"
// #include "lwip/sockets.h"
// #include "lwip/dns.h"
// #include "lwip/netdb.h"




#include "esp_smartconfig.h"
#include "smartconfig_ack.h"




static const char *TAG = "HsIot";
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");








// 通用变量
#define currVer "2008191111"
uint8_t mac[6];

//smartconfig 常量变量函数定义声明
static const int ESPTOUCH_DONE_BIT = BIT1;

void smartconfig_task(void * parm);
static void smartconfig_callback(smartconfig_status_t status, void *pdata);


//wifi 常量变量函数定义声明
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
const int CONNECTED_BIT = BIT0;


static wifi_config_t wifi_configDef1 = {
        .sta = {
            .ssid = "MoolinkIot",
            .password = "#moolink@iot",
        },
    };
static wifi_config_t wifi_configDef0 = {
        .sta = {
            .ssid = "HuansiIot",
            .password = "#huansi@iot",
        },
    };

wifi_config_t *wifi_config[2];
int wifi_init_case_cnt=0;


static void wifi_init(void);
static esp_err_t wifi_event_handler(void *ctx, system_event_t *event);



//mqtt 常量变量函数定义声明
static char topicname[50];
static char debugtopicname[50];
static char MacHexStr[15];
static char recvtopic[50];
static char recvjson[300];
static char pubjson[500];
static char datajson[200];

static esp_mqtt_client_handle_t mqttclient;


int hexStr2Bytes( unsigned char *output,  char *input, int ilen );
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event);
static int mqtt_jsonobj_process(char * JsonRoot);
static void mqtt_app_start(void);
static void mqtt_app_stop(void);

//ota 常量变量函数定义声明
static char otaupdateurl[150];
static char stcupdateurl[150];
static void ota_app_start(void);

static void stc_ota_app_start(void);

void moolink_ota_task(void * pvParameter);
esp_err_t _http_event_handler(esp_http_client_event_t *evt);


//uart 常量变量函数定义声明
#define EX_UART_NUM UART_NUM_0
#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE)
static QueueHandle_t uart0_queue;
static unsigned char uartsendbuffer[100];

unsigned int GetCRC16(unsigned char* database, unsigned char length);
static void uart_app_start(void);
static void uart_event_task(void *pvParameters);














#define HEX2NUM( c )                        \
    do                                      \
    {                                       \
        if( (c) >= '0' && (c) <= '9' )      \
            (c) -= '0';                     \
        else if( (c) >= 'a' && (c) <= 'f' ) \
            (c) -= 'a' - 10;                \
        else if( (c) >= 'A' && (c) <= 'F' ) \
            (c) -= 'A' - 10;                \
        else                                \
            return( -1 );                   \
    } while( 0 )

    

int hexStr2Bytes( unsigned char *output,  char *input, int ilen )
{
    unsigned char c;
    unsigned char olen;
    size_t j;

    // *olen = strlen( input );
    // if( *olen % 2 != 0 || *olen / 2 > MBEDTLS_PSK_MAX_LEN )
    //     return( -1 );
    olen=ilen / 2;

    for( j = 0; j < olen * 2; j += 2 )
    {
        c = input[j];
        HEX2NUM( c );
        output[ j / 2 ] = c << 4;

        c = input[j + 1];
        HEX2NUM( c );
        output[ j / 2 ] |= c;
    }

    return( 0 );
}




static void smartconfig_callback(smartconfig_status_t status, void *pdata)
{
    switch (status) {
        case SC_STATUS_WAIT:
            ESP_LOGI(TAG, "SC_STATUS_WAIT");
            break;
        case SC_STATUS_FIND_CHANNEL:
            ESP_LOGI(TAG, "SC_STATUS_FINDING_CHANNEL");
            break;
        case SC_STATUS_GETTING_SSID_PSWD:
            ESP_LOGI(TAG, "SC_STATUS_GETTING_SSID_PSWD");
            break;
        case SC_STATUS_LINK:
            ESP_LOGI(TAG, "SC_STATUS_LINK");
            wifi_config_t *wifi_config_sc = pdata;
            ESP_LOGI(TAG, "SSID:%s", wifi_config_sc->sta.ssid);
            ESP_LOGI(TAG, "PASSWORD:%s", wifi_config_sc->sta.password);
            ESP_ERROR_CHECK( esp_wifi_disconnect() );
            wifi_init_case_cnt=0;
            ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));//WIFI_STORAGE_RAM
            ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_config_sc) );
            ESP_ERROR_CHECK( esp_wifi_connect());
            break;
        case SC_STATUS_LINK_OVER:
            ESP_LOGI(TAG, "SC_STATUS_LINK_OVER");
            if (pdata != NULL) {
                sc_callback_data_t *sc_callback_data = (sc_callback_data_t *)pdata;
                switch (sc_callback_data->type) {
                    case SC_ACK_TYPE_ESPTOUCH:
                        ESP_LOGI(TAG, "Phone ip: %d.%d.%d.%d", sc_callback_data->ip[0], sc_callback_data->ip[1], sc_callback_data->ip[2], sc_callback_data->ip[3]);
                        ESP_LOGI(TAG, "TYPE: ESPTOUCH");
                        break;
                    case SC_ACK_TYPE_AIRKISS:
                        ESP_LOGI(TAG, "TYPE: AIRKISS");
                        break;
                    default:
                        ESP_LOGE(TAG, "TYPE: ERROR");
                        break;
                }
            }
            xEventGroupSetBits(wifi_event_group, ESPTOUCH_DONE_BIT);
            break;
        default:
            break;
    }
}

void smartconfig_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS) );
    ESP_ERROR_CHECK( esp_smartconfig_start(smartconfig_callback));
    while (1) {
        uxBits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY); 
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
}



static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
    esp_mqtt_client_handle_t client = event->client;
    int msg_id=0;

    // your_context_t *context = event->context;
    switch (event->event_id) {
        case MQTT_EVENT_CONNECTED:
            ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
            sprintf(datajson, "{\"HM\":\"HSWIFI01A\",\"HV\":\"20051619\",\"SV\":\"%s\",\"DM\":\"HsSewing103\",\"Timestamp\":\"%d\"}",currVer,1597277287);
            sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 1, 1, 1, datajson);
            msg_id = esp_mqtt_client_publish(client, "/huansi/iot/data",  pubjson, 0, 1, 0);
            //msg_id = esp_mqtt_client_publish(client, "/sys/a10TzrmOwtF/ABCDEF01/thing/event/property/post", "{\"id\": \"123\", \"version\": \"1.0\", \"params\": {\"Workingswitch\": 0}, \"method\": \"thing.event.property.post\"}", 0, 0, 0);
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

            sprintf(topicname,"/huansi/iot/%s",MacHexStr);
            msg_id = esp_mqtt_client_subscribe(client, topicname, 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

            sprintf(debugtopicname,"/huansi/iot/%s/debug",MacHexStr);
            msg_id = esp_mqtt_client_subscribe(client, debugtopicname, 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);



            // msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
            // ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

            // msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
            // ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);



            break;
        case MQTT_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
            break;

        case MQTT_EVENT_SUBSCRIBED:
            ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
            //msg_id = esp_mqtt_client_publish(client, "/huansi/iot/data", "{\"Id\":\"[DeviceId]\",\"MsgType\":\"[MsgType]\",\"MsgId\":\"[MsgId]\",\"ReqId\":\"[ReqId]\",\"Data\": {\"Timestamp\":\"1597277287\"}}", 0, 0, 0);
            //msg_id = esp_mqtt_client_publish(client, "/sys/a10TzrmOwtF/ABCDEF01/thing/event/property/post", "{\"Workingswitch\": 0}", 0, 0, 0);

            
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
            break;
        case MQTT_EVENT_UNSUBSCRIBED:
            ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_PUBLISHED:
            ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "MQTT_EVENT_DATA");

            sprintf(recvtopic,"%.*s", event->topic_len, event->topic);

                // printf("recTOPIC=%s\r\n", recvtopic);
                // printf("subTOPIC=%s\r\n", topicname);
                // printf("degTOPIC=%s\r\n", debugtopicname);


            if(0==strcmp(recvtopic, topicname))
            {  
                
                sprintf(recvjson,"%.*s", event->data_len, event->data);
                mqtt_jsonobj_process(recvjson);
                // printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
                // printf("DATA=%.*s\r\n", event->data_len, event->data);
            }
            else if(0==strcmp(recvtopic, debugtopicname))
            {             
                hexStr2Bytes(uartsendbuffer,event->data,event->data_len);
                uart_write_bytes(EX_UART_NUM, (const char *)uartsendbuffer, event->data_len/2);
                // for (int i = 0; i < event->data_len - 1; i += 2) {
                // printf("0x%c%c\n", event->data[i], event->data[i+1]);
                // }
            }
            else
            {
                printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
                printf("DATA=%.*s\r\n", event->data_len, event->data);
            }

            break;
        case MQTT_EVENT_ERROR:
            ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
            break;
    }
    return ESP_OK;
}




static int mqtt_jsonobj_process(char * JsonRoot)
{
    int MsgTypeNum=0;
        //首先整体判断是否为一个json格式的数据
	cJSON *pJsonRoot = cJSON_Parse(JsonRoot);

    cJSON *pMsgType = cJSON_GetObjectItem(pJsonRoot, "MsgType");

    cJSON *pData = cJSON_GetObjectItem(pJsonRoot, "Data");

    cJSON *pOutput;
	//如果是否json格式数据
	if (pJsonRoot !=NULL) {



        if (cJSON_IsString(pMsgType))
        {
            MsgTypeNum = atoi(pMsgType->valuestring);

            switch (MsgTypeNum)
            {

                
             case 100: //下发时间数据：UTC时间总秒数
                /* code */
                break;
            case 3001: //确认上报超功率阀值计时时长，并允许采集器清零己上报数据
                /* code */
                break;
             case 3009: //确认上报清零请求
                /* code */
                break;
             case 3900: //确认手动报修上报事件
                /* code */
                break;
             case 4010: //查询当前温湿度信息
                /* code */
                break;
              case 4011: //查询当前输入输出端口状态 01 02 00 00 00 04 79 C9
                uartsendbuffer[0]=0x01; uartsendbuffer[1]=0x02; uartsendbuffer[2]=0x00; uartsendbuffer[3]=0x00; uartsendbuffer[4]=0x00; uartsendbuffer[5]=0x04; uartsendbuffer[6]=0x79; uartsendbuffer[7]=0xC9; 
                uart_write_bytes(EX_UART_NUM, (const char *)uartsendbuffer, 8);
                break;
              case 4012: //查询当前功率计时阀值
                /* code */
                break;
              case 6001: //切换ESP软件版本
                 pOutput = cJSON_GetObjectItem(pData, "NSV");
                  if (pOutput) {
                    if (cJSON_IsString(pOutput))
                    {
                        sprintf(otaupdateurl, "https://update.moolink.cn:10443/huansi/wifi01a/esp8266/esp_%s.bin", pOutput->valuestring);
                        sprintf(datajson, "{\"HM\":\"HSWIFI01A\",\"CSV\":\"%s\",\"NSV\":\"%s\",\"url\":\"%s\",\"Timestamp\":\"%d\"}", currVer, pOutput->valuestring,otaupdateurl,1597277287);
                        sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 7001, 1, 1, datajson);
                        esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);

                        ota_app_start();
                    }
        		}              
                break;
              case 6002: //切换STC软件版本
                 pOutput = cJSON_GetObjectItem(pData, "NHV");
                  if (pOutput) {
                    if (cJSON_IsString(pOutput))
                    {
                        sprintf(stcupdateurl, "https://update.moolink.cn:10443/huansi/wifi01a/stc8g/stc_%s.bin", pOutput->valuestring);
                        sprintf(datajson, "{\"HM\":\"HSWIFI01A\",\"CHV\":\"%s\",\"NHV\":\"%s\",\"url\":\"%s\",\"Timestamp\":\"%d\"}", currVer, pOutput->valuestring,stcupdateurl,1597277287);
                        sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 7002, 1, 1, datajson);
                        esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);

                        stc_ota_app_start();
                    }
        		}              
                break;


              case 6011: //输入输出口控制 01 05 00 00 00 01 0C 0A 

                pOutput = cJSON_GetObjectItem(pData, "Output");

                if (pOutput) {

                    if (cJSON_IsString(pOutput))
                    {
                        uartsendbuffer[0]=0x01; uartsendbuffer[1]=0x05; uartsendbuffer[2]=0x00; uartsendbuffer[3]=0x00; uartsendbuffer[4]=0x00; 
                        if(0==atoi(pOutput->valuestring))
                        {
                            uartsendbuffer[5]=0x00; uartsendbuffer[6]=0xCD; uartsendbuffer[7]=0xCA; 
                        }
                        else
                        {
                            uartsendbuffer[5]=0x01; uartsendbuffer[6]=0x0C; uartsendbuffer[7]=0x0A; 
                        }
                        uart_write_bytes(EX_UART_NUM, (const char *)uartsendbuffer, 8);
                    }

        		}


                break;
              case 6012: //设置功率预设阀值参数
                /* code */
                break;
            
            default:
                break;
            }



        }

       
		// if (pValue) {
		// 	//进一步剖析里面的name字段:注意这个根节点是 pValue
		// 	cJSON *pName = cJSON_GetObjectItem(pValue, "name");
		// 	if (pName)
		// 		if (cJSON_IsString(pName))
		// 			os_printf("get value->Name : %s \n", pName->valuestring);

		// 	//进一步剖析里面的age字段:注意这个根节点是 pValue
		// 	cJSON *pAge = cJSON_GetObjectItem(pValue, "age");
		// 	if (pAge)
		// 		if (cJSON_IsNumber(pAge))
		// 			os_printf("get value->Age : %d \n", pAge->valueint);


		// 	//进一步剖析里面的blog字段:注意这个根节点是 pValue
		// 	cJSON *pBlog= cJSON_GetObjectItem(pValue, "blog");
		// 	if (pBlog)
		// 		if (cJSON_IsString(pBlog))
		// 		os_printf("get value->pBlog	 : %s \n", pBlog->valuestring);
	    // }

        //printf("IsJson=%s\r\n", JsonRoot);
        return 1;
	}
    else
    {
        //printf("NotJson=%s\r\n", JsonRoot);
        return 0;
    }

}



unsigned int GetCRC16(unsigned char* database, unsigned char length) 
{ 
	int j; 
	unsigned int reg_crc=0Xffff; 
	while(length--) 
	{ 
		reg_crc ^= *database++; 
		for (j=0;j<8;j++) 
		{ 
			if (reg_crc & 0x01) reg_crc=(reg_crc>>1) ^ 0Xa001; /* LSB(b0)=1 */ 
			else reg_crc=reg_crc >>1; 
		} 
	} 
	return reg_crc;
} 



static void uart_event_task(void *pvParameters)
{
    uart_event_t event;
    uint8_t *dtmp = (uint8_t *) malloc(RD_BUF_SIZE);
    int regaddr=0;
    int pos = 0;
    for (;;) {
        // Waiting for UART event.
        if (xQueueReceive(uart0_queue, (void *)&event, (portTickType)portMAX_DELAY)) {
            bzero(dtmp, RD_BUF_SIZE);
            ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);

            switch (event.type) {
                // Event of UART receving data
                // We'd better handler data event fast, there would be much more data events than
                // other types of events. If we take too much time on data event, the queue might be full.
                case UART_DATA:
                    ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                    uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
                    ESP_LOGI(TAG, "[DATA EVT]:");
                    //uart_write_bytes(EX_UART_NUM, (const char *) dtmp, event.size);

                    if(0==GetCRC16(dtmp,event.size))
                    {
                        switch (dtmp[1])
                        {
                        case 0x02:
                             if(dtmp[2]==0x01)
                             {
                                //输入输出 //{"Input":"6","Output":"1","Timestamp":"1597277287"}
                                sprintf(datajson, "{\"Input\":\"%d\",\"Output\":\"%d\",\"Timestamp\":\"%d\"}",dtmp[3]&0x0f,(dtmp[3]>>4)&0x0f, 1597277287);
                                sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 5011, 1, 1, datajson);
                                esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);
                             }

                            break;
                        case 0x03:
                            regaddr=dtmp[2]*256+dtmp[3];
                            if(regaddr==0x0104)
                            {
                                //功率计时阀值 //{"UpThreshold":"100","DwThreshold":"200","Unit":"W","Timestamp":"1597277287"}
                                sprintf(datajson, "{\"UpThreshold\":\"%d\",\"DwThreshold\":\"%d\",\"Unit\":\"W\",\"Timestamp\":\"%d\"}",100,50, 1597277287);
                                sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 5012, 1, 1, datajson);
                                esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);
                            }
                            else if(regaddr==0x0208)
                            {
                                //温湿度 //{"Tmp":"275","TmpUnit":"C","Hum":"562","HumUnit":"%RH","Timestamp":"1597277287"}
                                sprintf(datajson, "{\"Tmp\":\"%d\",\"TmpUnit\":\"C\",\"Hum\":\"%d\",\"HumUnit\":\"RH\",\"Timestamp\":\"%d\"}",275, 561, 1597277287);
                                sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 5010, 1, 1, datajson);
                                esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);
                                
                            }
                            else if(regaddr==0x0210)
                            {
                                //开机运时间与心跳  //{"OnSec":"32654","RunSec":"20051619""Timestamp":"1597277287"}
                                sprintf(datajson, "{\"OnSec\":\"%d\",\"RunSec\":\"%d\",\"Timestamp\":\"%d\"}",123,456,1597277287);
                                sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": %s}", MacHexStr, 9, 1, 1, datajson);
                                esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);
                            }


                            break;
                        
                        default:
                            pos = 0;
                            for(int j = 0; j < (event.size); j++)
                                pos += sprintf(datajson+pos, "%02X", dtmp[j]);
                            datajson[pos] = '\0';//将最后一个字符'-'转换为'\n'
                            sprintf(pubjson, "{\"Id\":\"%s\",\"MsgType\":\"%04d\",\"MsgId\":\"%04d\",\"ReqId\":\"%04d\",\"Data\": {\"UartDat\":\"%s\"}}", MacHexStr, 0, 1, 1, datajson);
                            esp_mqtt_client_publish(mqttclient, "/huansi/iot/data",  pubjson, 0, 1, 0);
                            break;
                        }

                    }
                    else
                    {
                        pos = 0;
                        for(int j = 0; j < (event.size); j++)
                            pos += sprintf(datajson+pos, "%02X", dtmp[j]);
                        datajson[pos] = '\0';//将最后一个字符'-'转换为'\n'
                        esp_mqtt_client_publish(mqttclient, "/huansi/iot/data/debug",  datajson, 0, 1, 0);
                    }

                    break;

                // Event of HW FIFO overflow detected
                case UART_FIFO_OVF:
                    ESP_LOGI(TAG, "hw fifo overflow");
                    // If fifo overflow happened, you should consider adding flow control for your application.
                    // The ISR has already reset the rx FIFO,
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart0_queue);
                    break;

                // Event of UART ring buffer full
                case UART_BUFFER_FULL:
                    ESP_LOGI(TAG, "ring buffer full");
                    // If buffer full happened, you should consider encreasing your buffer size
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart0_queue);
                    break;

                case UART_PARITY_ERR:
                    ESP_LOGI(TAG, "uart parity error");
                    break;

                // Event of UART frame error
                case UART_FRAME_ERR:
                    ESP_LOGI(TAG, "uart frame error");
                    break;

                // Others
                default:
                    ESP_LOGI(TAG, "uart event type: %d", event.type);
                    break;
            }
        }
    }

    free(dtmp);
    dtmp = NULL;
    vTaskDelete(NULL);
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
    }
    return ESP_OK;
}

static esp_err_t wifi_event_handler(void *ctx, system_event_t *event)
{
    /* For accessing reason codes in case of disconnection */
    system_event_info_t *info = &event->event_info;
    
    switch (event->event_id) {
        case SYSTEM_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);

            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            ESP_LOGE(TAG, "Disconnect reason : %d", info->disconnected.reason);
            if (info->disconnected.reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) {
                /*Switch to 802.11 bgn mode */
                esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
            }
            
            ESP_ERROR_CHECK(esp_wifi_disconnect());

            wifi_init_case_cnt++;

            if(wifi_init_case_cnt<10)
            {
                esp_wifi_connect();
            } 
            else if(wifi_init_case_cnt==10)
            {
                ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
                ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_config[0]));
                ESP_ERROR_CHECK(esp_wifi_connect());
                ESP_LOGI(TAG, "connect the WIFI SSID:[%s]", (*wifi_config[0]).sta.ssid);
            }
            else if(wifi_init_case_cnt==11)
            {
                //ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
                ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_config[1]));
                ESP_ERROR_CHECK(esp_wifi_connect());
                ESP_LOGI(TAG, "connect the WIFI SSID:[%s]", (*wifi_config[1]).sta.ssid);
            }
             else if(wifi_init_case_cnt==12)
            {
                xTaskCreate(smartconfig_task, "smartconfig_task", 4096, NULL, 3, NULL);
            }           
            else
            {
                // ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));//WIFI_STORAGE_RAM
                // wifi_init_case_cnt=0;
            }
            

            xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
            break;
        default:
            break;
    }
    return ESP_OK;
}


static void wifi_init(void)
{

    wifi_config[0]=&wifi_configDef0;
    wifi_config[1]=&wifi_configDef1;

    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL));
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    //ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));//WIFI_STORAGE_RAM
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_configDef));
    // ESP_LOGI(TAG, "start the WIFI SSID:[%s]", wifi_configDef.sta.ssid);
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "Waiting for wifi");
    xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
}

void moolink_ota_task(void * pvParameter)
{

    mqtt_app_stop();

    ESP_LOGI(TAG, "Starting OTA HsIot...");
    
    /* Wait for the callback to set the CONNECTED_BIT in the
       event group.
    */
    xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
                        false, true, portMAX_DELAY);
    ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server....");
    
    esp_http_client_config_t config = {
        .url = otaupdateurl,//CONFIG_FIRMWARE_UPGRADE_URL,
        .cert_pem = (char *)server_cert_pem_start,
        .event_handler = _http_event_handler,
    };
    esp_err_t ret = esp_https_ota(&config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        ESP_LOGE(TAG, "Firmware Upgrades Failed");
    }

    esp_restart();

    // mqtt_app_start();

    // vTaskDelete(NULL);
    
    // while (1) {
    //     vTaskDelay(1000 / portTICK_PERIOD_MS);
    // }
}


void moolink_stc_ota_task(void * pvParameter)
{

    mqtt_app_stop();

    ESP_LOGI(TAG, "Starting STC-OTA HsIot...");
    
    /* Wait for the callback to set the CONNECTED_BIT in the
       event group.
    */
    xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
                        false, true, portMAX_DELAY);
    ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server....");
    
    esp_http_client_config_t config = {
        .url = stcupdateurl,//CONFIG_FIRMWARE_UPGRADE_URL,
        .cert_pem = (char *)server_cert_pem_start,
        .event_handler = _http_event_handler,
    };
    esp_err_t ret = stc_https_ota(&config);
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "Stc Firmware Upgrades OK");
    } else {
        ESP_LOGE(TAG, "Stc Firmware Upgrades Failed");
    }

    esp_restart();

    // mqtt_app_start();

    // vTaskDelete(NULL);
    
    // while (1) {
    //     vTaskDelay(1000 / portTICK_PERIOD_MS);
    // }
}




static void mqtt_app_start(void)
{
    esp_mqtt_client_config_t mqtt_cfg = {
        .uri = "mqtt://iot1.huansi.net",
        .event_handle = mqtt_event_handler,
        .client_id = MacHexStr,
        .username = "hsiot",
        .password = "hsiot",
        // .user_context = (void *)your_context
    };
    
    mqttclient = esp_mqtt_client_init(&mqtt_cfg);
    esp_mqtt_client_start(mqttclient);
}

static void mqtt_app_stop(void)
{
    esp_mqtt_client_unsubscribe(mqttclient,topicname);
    esp_mqtt_client_unsubscribe(mqttclient,debugtopicname);
    esp_mqtt_client_stop(mqttclient);
}



static void uart_app_start(void)
{
    uart_config_t uart_config = {
        .baud_rate = 9600,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };
    uart_param_config(EX_UART_NUM, &uart_config);

    // Install UART driver, and get the queue.
    uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 100, &uart0_queue, 0);

     xTaskCreate(uart_event_task, "uart_event_task", 2048, NULL, 12, NULL);
}

static void ota_app_start(void)
{
    xTaskCreate(&moolink_ota_task, "moolink_ota_task", 8192, NULL, 5, NULL);
}

static void stc_ota_app_start(void)
{
    xTaskCreate(&moolink_stc_ota_task, "moolink_stc_ota_task", 8192, NULL, 5, NULL);
}




void app_main()
{

    // ESP_LOGI(TAG, "[APP] Startup..");
    // //ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
    // ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());


    // //获取从未使用过的最小内存
    //  ESP_LOGI(TAG, "esp_get_minimum_free_heap_size : %d  \n", esp_get_minimum_free_heap_size());
    // //获取芯片的内存分布，返回值具体见结构体 flash_size_map
    //  ESP_LOGI(TAG, "system_get_flash_size_map(): %d \n", system_get_flash_size_map());
    // //获取mac地址（station模式）


    // Initialize NVS.
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
        // OTA app partition table has a smaller NVS partition size than the non-OTA
        // partition table. This size mismatch may cause NVS initialization to fail.
        // If this happens, we erase NVS partition and initialize NVS again.
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK( err );

    //获取mac地址（station模式）
    esp_read_mac(mac, ESP_MAC_WIFI_STA);
    //初始化MAC字串，用作client_id
    sprintf(MacHexStr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    //初始化ota地址字串
    sprintf(otaupdateurl, "https://update.moolink.cn:10443/huansi/wifi01a/esp8266/hsiot_%s.bin", "2008182323");

    wifi_init();

    uart_app_start();

    mqtt_app_start();




}
